#include "ReceiveResult.h"
#include "TestCase.h"

#include <iostream>
#include <sstream>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

using namespace std;

const static int MAX_RECV_BUF_SIZE = 1024 * 10;

/*******************************************************************************
*  FUNC     :   构造函数初始化一些成员变量
*  ARGS     :   oneCase (class TestCase，需要验证的case)
                listen  (接收结果的监听端口)
                timeout (接收结果的超时时间s)
*  RTN      :
*  NOTE     :
*******************************************************************************/
ReceiveResult::ReceiveResult(TestCase &oneCase, int listen, int timeout)
    :oneCase_(oneCase), sock_(-1), finished_(false), cmpIdx_(0)
     , saveResultMode_(false), listen_(listen), timeout_(timeout)
{
}

/********************************************************************************
*  FUNC     :   析构函数释放socket句柄
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
ReceiveResult::~ReceiveResult()
{
    if (sock_ >= 0)
        close(sock_);
}

/********************************************************************************
*  FUNC     :   线程处理函数
*  ARGS     :
*  RTN      :
*  NOTE     :   接收解析服务器返回的结果
*******************************************************************************/
void ReceiveResult::DoProcess()
{
    if (!PreSocket())
    {
        cout << "prepare socket failed" << endl;

        return;
    }

    finished_ = false;
    string results;

    struct sockaddr_in cliAddr;
    char recvBuffer[MAX_RECV_BUF_SIZE];
    unsigned int addrLen = sizeof(cliAddr);
    FILE *fd = fopen("result.log", "w");

    while(1)
    {
        int cfd = accept(sock_, (struct sockaddr*)&cliAddr, &addrLen);
        if(cfd < 0)
        {
            //cout << "accept failed(" << errno << "): " << strerror(errno) << endl;
            oneCase_.SetPassed(false);
            setFailedReason("timeout");
            break;
        }

        results.clear();
        while (1)
        {
            int recvLen = recvfrom(cfd, recvBuffer, MAX_RECV_BUF_SIZE, 0,
                    (struct sockaddr*)&cliAddr, &addrLen);

            if (recvLen < 0)
            {
                //cout << "reveive msg failed" << endl;
                oneCase_.SetPassed(false);
                setFailedReason("timeout");
                //cout << "client close recvLen: " << recvLen << endl;
                close(cfd);
                fclose(fd);
                return;
            }
            else if(recvLen == 0)
            {
                //cout << "client close recvLen: " << recvLen << endl;
                close(cfd);
                //return;
                break;
            }

            //收集接收到的数据保存到results中
            //cout << "====" << recvLen << endl;
            fwrite(recvBuffer, sizeof(char), recvLen, fd);
            fflush(fd);
            results.append(recvBuffer, recvLen);
            //cout << "====results:====" << endl
            //    << results << endl
            //    << "========" << results.size() << endl;

            //解析并比对结果
            ParseResult(results);

            //检查正确和错误结果的个数
            if (finished_)
            {
                //cout << "client close finished" << endl;
                close(cfd);
                break;
            }
        }

        if (finished_)
            break;
    }

    fclose(fd);
    return;
}

/********************************************************************************
*  FUNC     :   结果比对
*  ARGS     :   resExpected (期望的结果)
                resCurrent  (实际获得的结果)
*  RTN      :   结果一直: true, 结果不一致: false
*  NOTE     :
*******************************************************************************/
bool ReceiveResult::CompareResult(const string &resExpected,
        const string &resCurrent)
{
    if (resExpected.size() != resCurrent.size())
        return false;

    for (uint32_t i = 0; i < resExpected.size(); i++)
    {
        if (resExpected[i] != resCurrent[i])
            return false;
    }

    return true;
}

/********************************************************************************
*  FUNC     :   准备好接收服务器socket,监听给定的端口号
*  ARGS     :
*  RTN      :
*  NOTE     :   UDP方式接收
*******************************************************************************/
bool ReceiveResult::PreSocket()
{
    sock_ = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_ < 0)
        return false;


    int option = 1;
    int ret = setsockopt(sock_, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(int));
    if(ret < 0)
    {
        cout << "set socket reuseaddr failed ret: " << ret << endl;
    }

    struct sockaddr_in svrAddr;
    memset(&svrAddr, 0, sizeof(svrAddr));
    svrAddr.sin_family = AF_INET;
    svrAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    svrAddr.sin_port = htons(listen_);
    if (bind (sock_, (struct sockaddr*)&svrAddr, sizeof(svrAddr)) < 0)
    {
        cout << "bind failed(" << errno << "): " << strerror(errno) << endl;
        return false;
    }

    struct timeval tv_out;
    tv_out.tv_sec = timeout_;
    tv_out.tv_usec = 0;

    ret = setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));
    if (ret < 0)
    {
        cout << "set socket timeout failed ret: " << ret << endl;
    }

    ret = listen(sock_, 10);
    if(ret < 0)
    {
        cout << "listen failed ret: " << ret << ", " << strerror(errno) << endl;
        return false;
    }

    return true;
}

/********************************************************************************
*  FUNC     :   解析结果
*  ARGS     :   results (结果缓冲池)
*  RTN      :
*  NOTE     :   协议方式: 长度:结果 例如：5:data1
*******************************************************************************/
void ReceiveResult::ParseResult(string &results)
{
    if(finished_) return;

    size_t flagPos = results.find(":");
    if (flagPos == string::npos)
    {
        //没有找到标志符,不符合协议
        results.clear();
        return;
    }

    size_t flagPos2 = results.find("\n");
    string strLen;
    if (flagPos2 == string::npos || flagPos2 >= flagPos)
        strLen = string(results, 0, flagPos);
    else
        strLen = string(results, flagPos2+1, flagPos-flagPos2-1);

    int dateLen = atoi(strLen.c_str());
    if (dateLen > (int)(results.size() - flagPos - 1))   //数据还不够，还需要再等待
        return;

    string resCurrent(results, flagPos+1, dateLen);
    cout << "compareDateLen: " << dateLen << endl; //", result:" << endl<< resCurrent << endl;
    if (saveResultMode_)
    {
        oneCase_.AddCompareData(resCurrent);
    }
    else
    {
        uint32_t cmpDataNum = oneCase_.GetCompareDatasNum();
        if(cmpDataNum == 0)
        {
            finished_ = true;
            cout << "case has no compare data" << endl;
            oneCase_.SetPassed(true);

            return;
        }

        string resExpected = oneCase_.GetCompareData(cmpIdx_);
        if (CompareResult(resExpected, resCurrent))
        {
            cmpIdx_++;
            if (cmpIdx_ >= cmpDataNum)
            {
                //已经比对完了
                finished_ = true;
                oneCase_.SetPassed(true);
                return;
            }
        }
        else
        {
            finished_ = true;
            oneCase_.SetPassed(false);
            ostringstream oss;
            oss << "<<<<<<expected\n" << resExpected << "\n<<<<<<\n"
                << "<<<<<<current\n" << resCurrent << "\n<<<<<<";
            string reason = oss.str();
            setFailedReason(reason);
            return;
        }
    }

    //继续比对剩余的数据
    size_t leftStart = flagPos+dateLen+1;
    int leftLen = results.size() - leftStart;
    results = string(results, leftStart, leftLen);
    ParseResult(results);
}

/********************************************************************************
*  FUNC     :   设置是否为保存结果模式
*  ARGS     :   saveResultMode (true: 保存结果模式，false: 结果比对模式)
*  RTN      :
*  NOTE     :   协议方式: 长度:结果 例如：5:data1
*******************************************************************************/
void ReceiveResult::setSaveResultMode(bool saveResultMode)
{
    saveResultMode_ = saveResultMode;
}

/********************************************************************************
*  FUNC     :   设置失败原因
*  ARGS     :   reason (失败原因)
*  RTN      :
*  NOTE     :   协议方式: 长度:结果 例如：5:data1
*******************************************************************************/
void ReceiveResult::setFailedReason(const std::string &reason)
{
    uint32_t cmpDataNum = oneCase_.GetCompareDatasNum();

    ostringstream oss;
    if(reason != "timeout" || cmpIdx_ != 0)
        oss << "Failed: " << cmpIdx_ + 1 << "/" << cmpDataNum << endl;
    else
        oss << "Failed: " << endl;
    oss << reason << endl;

    oneCase_.SetReason(oss.str());
}
